﻿using Microsoft.Windows.Themes;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Threading;

namespace UBCon.Navigation
{

    public class NavigationItemsCollection : ObservableCollection<NavigationItem> { }

    /// <summary>
    /// Represents a selectable item in UBCon.Navigation.OutlookBar.
    /// </summary>
    public class OutlookBarItem : ListBoxItem
    {
        //------------------------------------------------------
        //
        //  Constructors
        //
        //------------------------------------------------------
        #region Constructors

        /// <summary>
        /// Default static constructor.
        /// </summary>
        static OutlookBarItem()
        {
            Type ownerType = typeof(OutlookBarItem);
            OutlookBarItem.DefaultStyleKeyProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(ownerType));
            OutlookBarItem.VisibilityProperty.OverrideMetadata(ownerType,
                new FrameworkPropertyMetadata(new PropertyChangedCallback(OnVisibilityPropertyChanged)));
        }

        /// <summary>
        /// Initializes a new instance of the UBCon.Navigation.OutlookBarItem class.
        /// </summary>
        public OutlookBarItem()
        {
            RenderTransform = translate;
            //BuildAnimation();

            timer.Interval = TimeSpan.FromMilliseconds(800);

            closeDropDowntimer.Interval = TimeSpan.FromMilliseconds(600);

            //By default help popup is enabled hence initially we specify evend handler.
            //when EnableDropDown property has a false value we remove these handler. 
            //See OnEnableDropDownPropertyChanged method.
            timer.Tick += timer_Tick;

            closeDropDowntimer.Tick += closeDropDowntimer_Tick;

            Mouse.AddPreviewMouseDownOutsideCapturedElementHandler(this, OnOutsideElementCaptured);

            NavigationItems = new NavigationItemsCollection();

            BindingOperations.SetBinding
                (
                    AllNavigationItem,
                    NavigationItem.ContentProperty,
                    new Binding("AllItemContent") { Source = this, Mode = BindingMode.OneWay }
                );

            AllNavigationItem.MouseLeftButtonUp += AllNavigationItem_MouseLeftButtonUp;

          //  Loaded += OutlookBarItemLoaded;

            BuildAlltemPoup();
        }

        /// <summary>
        /// Occurs when OutlookBarItem control is loaded.
        /// </summary>
        /// <param name="sender">Type: System.Object. Owner of the event.</param>
        /// <param name="e">Event data for the event.</param>
        private void OutlookBarItemLoaded(object sender, RoutedEventArgs e)
        {
            Loaded -= OutlookBarItemLoaded;


        }
        #endregion

        //------------------------------------------------------
        //
        //  Properties
        //
        //------------------------------------------------------
        #region Properties

        internal double Value
        {
            get { return (double)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Value.  This enables animation, styling, binding, etc...
        internal static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(double), typeof(OutlookBarItem), new FrameworkPropertyMetadata(0.0, new PropertyChangedCallback(OnValuePropertyChanged)));

        /// <summary>
        /// Gets or sets a value System.Windows.Controls.Primitives.PlacementMode for the this control.
        /// </summary>
        public PlacementMode Placement
        {
            get { return (PlacementMode)GetValue(PlacementProperty); }
            set { SetValue(PlacementProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBarItem.Placement dependency property.
        /// </summary>
        public static readonly DependencyProperty PlacementProperty =
            DependencyProperty.Register("Placement", typeof(PlacementMode), typeof(OutlookBarItem), new FrameworkPropertyMetadata(PlacementMode.Top));

        /// <summary>
        /// Gets or sets default NavigationItem control's content. This is a dependency property.
        /// </summary>
        public object AllItemContent
        {
            get { return (object)GetValue(AllItemContentProperty); }
            set { SetValue(AllItemContentProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBarItem.AllItemContent dependency property.
        /// </summary>
        public static readonly DependencyProperty AllItemContentProperty =
            DependencyProperty.Register("AllItemContent", typeof(object), typeof(OutlookBarItem), new FrameworkPropertyMetadata("All folders"));

        /// <summary>
        /// Gets or sets collection of NavigationItem control. 
        /// This is a dependency property.
        /// </summary>
        public NavigationItemsCollection NavigationItems
        {
            get { return (NavigationItemsCollection)GetValue(NavigationItemsProperty); }
            set { SetValue(NavigationItemsProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBarItem.NavigationItems dependency property.
        /// </summary>
        public static readonly DependencyProperty NavigationItemsProperty =
            DependencyProperty.Register("NavigationItems", typeof(NavigationItemsCollection), typeof(OutlookBarItem),
            new FrameworkPropertyMetadata((NavigationItemsCollection)null));

        /// <summary>
        /// Gets or sets a boolean value indicating whether help popup is open or not. 
        /// This is a depencency property.
        /// </summary>
        internal bool IsDropDownOpen
        {
            get { return (bool)GetValue(IsDropDownOpenProperty); }
            set { SetValue(IsDropDownOpenProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBarItem.IsDropDownOpen dependency property.
        /// </summary>
        internal static readonly DependencyProperty IsDropDownOpenProperty =
            DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(OutlookBarItem),
            new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsDropDownOpenPropertyChanged)));

        /// <summary>
        /// Gets or sets actual content of the parent window.
        /// This is a dependency property.
        /// </summary>
        [Bindable(false),
        ReadOnly(false),
        Browsable(false)]
        public UIElement InnerContent
        {
            get { return (UIElement)GetValue(InnerContentProperty); }
            set { SetValue(InnerContentProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBarItem.InnerContent dependency property.
        /// </summary>
        public static readonly DependencyProperty InnerContentProperty =
            DependencyProperty.Register("InnerContent", typeof(UIElement), typeof(OutlookBarItem),
            new FrameworkPropertyMetadata(null));

        /// <summary>
        /// Gets or sets content of the drop-down.
        /// This is a dependency property.
        /// </summary>
        [Bindable(false),
        ReadOnly(false),
        Browsable(false)]
        public UIElement DropDownContent
        {
            get { return (UIElement)GetValue(DropDownContentProperty); }
            set { SetValue(DropDownContentProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBarItem.DropDownContent dependency property.
        /// </summary>
        public static readonly DependencyProperty DropDownContentProperty =
            DependencyProperty.Register("DropDownContent", typeof(UIElement), typeof(OutlookBarItem), new
                FrameworkPropertyMetadata(null));

        /// <summary>
        /// Gets or sets title text for OutlookBarItem.
        /// This is dependency property.
        /// </summary>
        [Bindable(true),
        ReadOnly(false),
        Browsable(true),
        Category("Appearence")]
        public string Title
        {
            get { return (string)GetValue(TitleProperty); }
            set { SetValue(TitleProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBarItem.Title dependency property.
        /// </summary>
        public static readonly DependencyProperty TitleProperty =
            DependencyProperty.Register("Title", typeof(string), typeof(OutlookBarItem), new FrameworkPropertyMetadata(null));

        /// <summary>
        /// Gets or sets ImageSource for OutlookBarItem. 
        /// This is a dependency property.
        /// </summary>
        [Bindable(true),
        ReadOnly(false),
        Browsable(true),
        Category("Appearence")]
        public ImageSource Image
        {
            get { return (ImageSource)GetValue(ImageProperty); }
            set { SetValue(ImageProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBarItem.Image dependency property.
        /// </summary>
        public static readonly DependencyProperty ImageProperty =
            DependencyProperty.Register("Image", typeof(ImageSource), typeof(OutlookBarItem),
            new FrameworkPropertyMetadata((ImageSource)null));

        /// <summary>
        /// Gets or sets an instance of UBCon.Navigation.OutlookBar that own this control.
        /// This is a dependency property.
        /// </summary>
        internal OutlookBar ParentOutlook
        {
            get { return (OutlookBar)GetValue(ParentOutlookProperty); }
            set { SetValue(ParentOutlookProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBarItem.ParentOutlook dependency property.
        /// </summary>
        internal static readonly DependencyProperty ParentOutlookProperty =
            DependencyProperty.Register("ParentOutlook", typeof(OutlookBar), typeof(OutlookBarItem),
            new FrameworkPropertyMetadata(null));

        /// <summary>
        /// Gets or sets a value that contains horizontal offset of the drop-down's content.
        /// This is a dependency property.
        /// </summary>
        internal double HorizontalOffset
        {
            get { return (double)GetValue(HorizontalOffsetProperty); }
            set { SetValue(HorizontalOffsetProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBarItem.HorizontalOffset dependency property.
        /// </summary>
        internal static readonly DependencyProperty HorizontalOffsetProperty =
            DependencyProperty.Register("HorizontalOffset", typeof(double), typeof(OutlookBarItem), new FrameworkPropertyMetadata(0.0));

        /// <summary>
        /// Gets or sets a value that contains vertical offset of the drop-down's content.
        /// This is a dependency property.
        /// </summary>
        internal double VerticalOffset
        {
            get { return (double)GetValue(VerticalOffsetProperty); }
            set { SetValue(VerticalOffsetProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBarItem.VerticalOffset dependency property.
        /// </summary>
        internal static readonly DependencyProperty VerticalOffsetProperty =
            DependencyProperty.Register("VerticalOffset", typeof(double), typeof(OutlookBarItem), new FrameworkPropertyMetadata(0.0));

        /// <summary>
        /// Gets or sets a boolean value that indicates whether UBCon.Navigation.OutlookBarItem control has a drop-down or not. 
        /// This is a dependency property.
        /// </summary>
        [Bindable(true),
        ReadOnly(false),
        Browsable(false),
        Category("Behaviour")]
        public bool EnableDropDown
        {
            get { return (bool)GetValue(EnableDropDownProperty); }
            set { SetValue(EnableDropDownProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBarItem.EnableDropDown dependency property.
        /// </summary>
        public static readonly DependencyProperty EnableDropDownProperty =
            DependencyProperty.Register("EnableDropDown", typeof(bool), typeof(OutlookBarItem),
            new FrameworkPropertyMetadata(true));

        /// <summary>
        /// Gets or sets a boolean value that indicates whether initialized storyboards are valid or not.
        /// </summary>
        internal bool StoryboardsValid
        {
            get
            {
                return SmallScaleStoryborad != null && NormalScaleStoryboard != null;
            }
        }

        /// <summary>
        /// Gets or sets a value of the drop-down's height.
        /// </summary>
        [Bindable(true),
        ReadOnly(false),
        Browsable(false)]
        public double DropDownHeight
        {
            get { return (double)GetValue(DropDownHeightProperty); }
            set { SetValue(DropDownHeightProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBarItem.DropDownHeight dependency property.
        /// </summary>
        public static readonly DependencyProperty DropDownHeightProperty =
            DependencyProperty.Register("DropDownHeight", typeof(double), typeof(OutlookBarItem), new FrameworkPropertyMetadata(200.0D));

        /// <summary>
        /// Gets or sets a value of the drop-down's width.
        /// </summary>
        [Bindable(true),
        ReadOnly(false),
        Browsable(false)]
        public double DropDownWidth
        {
            get { return (double)GetValue(DropDownWidthProperty); }
            set { SetValue(DropDownWidthProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBarItem.DropDownWidth dependency property.
        /// </summary>
        public static readonly DependencyProperty DropDownWidthProperty =
            DependencyProperty.Register("DropDownWidth", typeof(double), typeof(OutlookBarItem), new FrameworkPropertyMetadata(200.0D));

        /// <summary>
        /// Gets a boolean value indicating whether OutlookBarItem is being dragged or not.
        /// </summary>
        [Bindable(true),
        Browsable(false),
        Category("Appearance")]
        public bool IsDragging
        {
            get { return (bool)GetValue(IsDraggingProperty); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBarItem.IsDragging dependency property.
        /// </summary>
        internal static readonly DependencyPropertyKey IsDraggingPropertyKey =
            DependencyProperty.RegisterReadOnly("IsDragging", typeof(bool), typeof(OutlookBarItem),
            new PropertyMetadata(false, new System.Windows.PropertyChangedCallback(OnIsDraggingPropertyChanged)));
        internal static readonly System.Windows.DependencyProperty IsDraggingProperty = IsDraggingPropertyKey.DependencyProperty;

        /// <summary>
        /// Indicates whether drop down content can be animated or not
        /// </summary>
        protected internal bool ContentCanBeAnimated
        {
            get
            {
                return popup != null
                       && PopupChild != null
                       && PopupChild is FrameworkElement;
            }
        }

        /// <summary>
        /// Gets drop-down's child element.
        /// </summary>
        internal UIElement PopupChild
        {
            get
            {
                return popup.Child;

            }
        }

        internal Size ActualSize = new Size();

        #endregion

        //------------------------------------------------------
        //
        //  Depenency Property Changed Callbacks
        //
        //------------------------------------------------------
        #region Depenency Property Changed Callbacks

        /// <summary>
        /// Invoked whenever IsDragging property changes.
        /// </summary>
        /// <param name="sender">Type: System.Windows.DependencyObject. The DependencyObject on which the property has changed value.</param>
        /// <param name="e">Type: System.Windows.DependencyPropertyChangedEventArgs. Event data that is issued by any event that tracks changes to the effective value of this property.</param>
        private static void OnIsDraggingPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { }

        /// <summary>
        /// Invoked whenever Value property changes.
        /// </summary>
        /// <param name="sender">Type: System.Windows.DependencyObject. The DependencyObject on which the property has changed value.</param>
        /// <param name="e">Type: System.Windows.DependencyPropertyChangedEventArgs. Event data that is issued by any event that tracks changes to the effective value of this property.</param>
        private static void OnValuePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            OutlookBarItem item = sender as OutlookBarItem;
            double newValue = (double)e.NewValue;
            if (!item.ParentOutlook.IsCompact || !item.ParentOutlook.IsMinimized)
            {
                item.VerticalOffset = newValue;
            }
            else
            {
                item.HorizontalOffset = newValue;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        //private static void OnEnableDropDownPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        //{
        //    OutlookBarItem barItem = sender as OutlookBarItem;
        //    bool newValue = (bool)e.NewValue;
        //    if (newValue)
        //    {
        //        barItem.timer.Tick += barItem.timer_Tick;
        //        barItem.closeDropDowntimer.Tick += barItem.closeDropDowntimer_Tick;
        //        Mouse.AddPreviewMouseDownOutsideCapturedElementHandler(barItem, barItem.OnOutsideElementCaptured);
        //    }
        //    else
        //    {
        //        barItem.timer.Tick -= barItem.timer_Tick;
        //        barItem.closeDropDowntimer.Tick -= barItem.closeDropDowntimer_Tick;

        //        Mouse.RemovePreviewMouseDownOutsideCapturedElementHandler(barItem, barItem.OnOutsideElementCaptured);
        //    }
        //}

        /// <summary>
        /// Invoked whenever IsDropDownOpen property changes.
        /// </summary>
        /// <param name="sender">Type: System.Windows.DependencyObject. The DependencyObject on which the property has changed value.</param>
        /// <param name="e">Type: System.Windows.DependencyPropertyChangedEventArgs. Event data that is issued by any event that tracks changes to the effective value of this property.</param>
        private static void OnIsDropDownOpenPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            OutlookBarItem barItem = (OutlookBarItem)sender;
            bool newValue = (bool)e.NewValue;
            bool oldValue = !newValue;
            if (newValue)
            {
                barItem.OnDropDownOpened();
            }
            else
            {
                barItem.OnDropDownClosed();

                if (barItem.image != null)
                {
                    barItem.image.Margin = new Thickness(0, -2, 0, 0);
                    barItem.image.HorizontalAlignment = HorizontalAlignment.Center;
                }

            }
        }

        /// <summary>
        /// Invoked whenever control's Visibility property changes.
        /// </summary>
        /// <param name="sender">Type: System.Windows.DependencyObject. The DependencyObject on which the property has changed value.</param>
        /// <param name="e">Type: System.Windows.DependencyPropertyChangedEventArgs. Event data that is issued by any event that tracks changes to the effective value of this property.</param>
        private static void OnVisibilityPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            OutlookBarItem barItem = sender as OutlookBarItem;

            DropDownMenuItem menuItem = barItem.GetValue(OutlookBar.DropDownMenuItemProperty) as DropDownMenuItem;

            if (barItem.ParentOutlook == null)
            {
                barItem.ParentOutlook = ItemsControl.ItemsControlFromItemContainer(barItem) as OutlookBar;
            }
            if (menuItem != null)
            {
                Visibility visibility = (Visibility)e.NewValue;

                if (visibility == Visibility.Visible && menuItem.Visibility == Visibility.Visible)
                {
                    menuItem.Visibility = Visibility.Collapsed;
                }
                else if (visibility == Visibility.Collapsed && menuItem.Visibility == Visibility.Collapsed)
                {
                    menuItem.Visibility = Visibility.Visible;
                }
            }
        }
        #endregion

        //------------------------------------------------------
        //
        //  Overrides
        //
        //------------------------------------------------------
        #region Overrides

        /// <summary>
        /// Raises the Initialized event. This method is invoked whenever IsInitialized is set to true.
        /// </summary>
        /// <param name="e">Event data for the event.</param>
        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);

            Window window = Window.GetWindow(this);

            if (window != null)
            {
                window.Closing += OnParentWindowClosing;
            }
        }

        /// <summary>
        /// Invoked whenever whenever mouse device moves.
        /// </summary>
        /// <param name="e">Event parameter.</param>
        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (IsDragging)
            {
                if (e.MouseDevice.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
                {
                    if (timer.IsEnabled)
                    {
                        timer.Stop();
                    }

                    if (IsDropDownOpen)
                    {
                        IsDropDownOpen = false;
                    }

                    Point thumbCoordPosition = e.GetPosition(this);

                    Point screenCoordPosition = ScreenToLocal(thumbCoordPosition);

                    if (screenCoordPosition != _previousScreenCoordPosition)
                    {
                        _previousScreenCoordPosition = screenCoordPosition;

                        e.Handled = true;

                        RaiseEvent(new UBCon.Navigation.DragDeltaEventArgs(thumbCoordPosition.X - _originThumbPoint.X,
                                                          thumbCoordPosition.Y - _originThumbPoint.Y));
                        itemMoved = true;

                        if (StoryboardsValid && scaleChanged)
                        {
                            scaleChanged = false;
                            NormalScaleStoryboard.Begin();
                        }
                    }
                }
                else
                {
                    if (e.MouseDevice.Captured == this)
                    {
                        ReleaseMouseCapture();
                    }
                    ClearValue(IsDraggingPropertyKey);

                    _originThumbPoint.X = 0;

                    _originThumbPoint.Y = 0;
                }
            }
            base.OnMouseMove(e);

        }

        /// <summary>
        /// Invoked when IsDropDownOpen property is set to true and raises UBCon.Navigation.OutlookBarItem.DropDownOpened event.
        /// </summary>
        public virtual void OnDropDownOpened()
        {
            RoutedEventArgs args = new RoutedEventArgs(DropDownOpenedEvent);
            RaiseEvent(args);
        }

        /// <summary>
        /// Invoked when IsDropDownOpen property is set to false and raises UBCon.Navigation.OutlookBarItem.DropDownClosed event.
        /// </summary>
        public virtual void OnDropDownClosed()
        {
            RoutedEventArgs args = new RoutedEventArgs(DropDownClosedEvent);
            RaiseEvent(args);
        }

        /// <summary>
        /// Occurs when the mouse pointer enters the bounds of this element.
        /// </summary>
        /// <param name="e">Event data for the event.</param>
        protected override void OnMouseEnter(MouseEventArgs e)
        {
            base.OnMouseEnter(e);

            if (rootControl == null && EnableDropDown)
            {
                if (closeDropDowntimer.IsEnabled)
                {
                    closeDropDowntimer.Stop();
                }
                timer.Start();
            }
        }

        /// <summary>
        /// Occurs when the left mouse button is pressed while the mouse pointer is over this element.
        /// </summary>
        /// <param name="e">Event data for the event.</param>
        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            //if root control is not null we make selection on root control click
            e.Handled = rootControl != null;

            if (!IsDragging)
            {
                Focus();

                itemMoved = false;

                //If popup window is pending to be open we must cancel it.
                if (timer.IsEnabled)
                {
                    timer.Stop();
                }

                CaptureMouse();

                SetValue(OutlookBarItem.IsDraggingPropertyKey, true);

                _originThumbPoint = e.GetPosition(this);

                _previousScreenCoordPosition = _originScreenCoordPosition = PointToScreen(_originThumbPoint);

                bool exceptionThrown = true;

                UBCon.Navigation.DragStartedEventArgs args = new UBCon.Navigation.DragStartedEventArgs(_originThumbPoint.X, _originThumbPoint.Y);

                try
                {
                    RaiseEvent(args);
                    exceptionThrown = false;

                }

                finally
                {
                    if (exceptionThrown)
                    {
                        CancelDrag();
                    }
                }
            }

            base.OnMouseLeftButtonDown(e);

        }

        /// <summary>
        /// Occurs when the mouse pointer leaves the bounds of this element.
        /// </summary>
        /// <param name="e">Event data for the event.</param>
        protected override void OnMouseLeave(MouseEventArgs e)
        {
            base.OnMouseLeave(e);

            if (rootControl == null)
            {
                if (timer.IsEnabled)
                {
                    timer.Stop();

                }
                if (!closeDropDowntimer.IsEnabled)
                {
                    closeDropDowntimer.Start();
                }
            }
        }

        /// <summary>
        /// Occurs when the left mouse button is released while the mouse pointer is over this element.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
        {
            e.Handled = rootControl != null;

            base.OnMouseLeftButtonUp(e);

            if (IsDragging && IsMouseCaptured)
            {
                if (!itemMoved)
                {
                    if (ParentOutlook == null)
                    {
                        ParentOutlook = ItemsControl.ItemsControlFromItemContainer(this) as OutlookBar;
                    }
                    if (ParentOutlook != null)
                    {
                        ParentOutlook.SelectedIndex = ParentOutlook.ItemContainerGenerator.IndexFromContainer(this);
                    }
                }
                //e.Handled = true;
                ClearValue(OutlookBarItem.IsDraggingPropertyKey);

                ReleaseMouseCapture();

                Point pt = ScreenToLocal(e.MouseDevice.GetPosition(this));

                RaiseEvent(new UBCon.Navigation.DragCompletedEventArgs(pt.X - _originScreenCoordPosition.X, pt.Y - _originScreenCoordPosition.Y, false));
            }


            if (scaleChanged)
            {
                this.Dispatcher.BeginInvoke(DispatcherPriority.Background,
                          new DispatcherOperationCallback(delegate(object param)
                          {
                              if (param != null)
                              {
                                  NormalScaleStoryboard.Begin();
                              }

                              return null;

                          }), NormalScaleStoryboard);
            }

        }

        /// <summary>
        /// Invoked whenever application code or internal processes (such as a rebuilding layout pass) call ApplyTemplate. 
        /// In simplest terms, this means the method is called just before a UI element displays in  app.  
        /// </summary>
        public override void OnApplyTemplate()
        {

            if (ShowContentStoryboard != null)
            {
                ShowContentStoryboard.Completed -= ShowContentStoryboard_Completed;

                ShowContentStoryboard.Children.Clear();

                ShowContentStoryboard = null;
            }

            if (rootControl != null)
            {
                if (StoryboardsValid)
                {
                    //NormalScaleStoryboard.Stop();

                    NormalScaleStoryboard.Children.Clear();

                    NormalScaleStoryboard = null;

                   // SmallScaleStoryborad.Stop();

                    SmallScaleStoryborad.Children.Clear();

                    SmallScaleStoryborad = null;
                }
                rootControl.MouseLeftButtonDown -= rootControl_MouseLeftButtonDown;

                rootControl.MouseEnter -= rootControl_MouseEnter;

                rootControl.MouseLeave -= rootControl_MouseLeave;
            }

            rootControl = GetTemplateChild(rootControlName) as FrameworkElement;

            if (rootControl != null)
            {
                if (rootControl is TextBlock)
                {
                    rootControl.RenderTransform = new ScaleTransform();

                    BuildScaleAnimation();

                    BuildScaleAnimation(false);
                }

                rootControl.MouseLeftButtonDown += rootControl_MouseLeftButtonDown;

                rootControl.MouseEnter += rootControl_MouseEnter;

                rootControl.MouseLeave += rootControl_MouseLeave;
            }
            if (popup != null)
            {
                popup.Opened -= popup_Loaded;

            }
            popup = GetTemplateChild(popupName) as Popup;

            if (popup != null)
            {
                popup.Opened += popup_Loaded;
            }

            image = GetTemplateChild(imageName) as Image;

            base.OnApplyTemplate();
        }

        /// <summary>
        /// Occurs when this element loses mouse capture.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnLostMouseCapture(System.Windows.Input.MouseEventArgs e)
        {
            if (Mouse.Captured != this)
            {
                CancelDrag();
            }

            base.OnLostMouseCapture(e);
        }

        #endregion

        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------
        #region Private Methods

        /// <summary>
        /// Converts screed based point into local.
        /// </summary>
        /// <param name="point"></param>
        /// <returns>System.Winddows.Point. Converted point.</returns>
        private Point ScreenToLocal(Point point)
        {
            HwndSource source = (HwndSource)PresentationSource.FromVisual(this);

            IntPtr handle = source.Handle;

            UBCon.Core.NativeMethods.RECT rect;

            UBCon.Core.NativeMethods.GetWindowRect(handle, out rect);

            Matrix matrix = PresentationSource.FromVisual(this).CompositionTarget.TransformFromDevice;

            point.Offset((double)(-1 * rect.Left), (double)(-1 * rect.Top));

            point.X *= matrix.M11;

            point.Y *= matrix.M22;

            return point;
        }

        /// <summary>
        /// Cancels drag operation for UBCon.Navigation.OutlookBarItem control.
        /// </summary>
        public void CancelDrag()
        {
            if (IsDragging)
            {
                if (IsMouseCaptured)
                {
                    ReleaseMouseCapture();
                }
            }
            SetValue(OutlookBarItem.IsDraggingPropertyKey, false);

            UBCon.Navigation.DragCompletedEventArgs deltaArgs = new UBCon.Navigation.DragCompletedEventArgs(_previousScreenCoordPosition.X - this._originScreenCoordPosition.X, this._previousScreenCoordPosition.Y - this._originScreenCoordPosition.Y, true);

            RaiseEvent(deltaArgs);
        }

        /// <summary>
        /// Builds popup window which is opened when AllItems button is pressed.
        /// </summary>
        private void BuildAlltemPoup()
        {
            shadowChrome = new SystemDropShadowChrome();

            Border border = new Border()
            {
                Margin = new Thickness(0, 0, 3, 3)
            };
            ComponentResourceKey key = new ComponentResourceKey(typeof(SkinManagers.SkinManager), "WhiteBrush");

            SolidColorBrush brush = NavigationHelper.GetSkinResourceByKey<SolidColorBrush>(key);
            
            if (brush == null)
            {
                brush = Brushes.White;
            }
            border.Background = brush;
            border.BorderThickness = new Thickness(1);
            key = new ComponentResourceKey(typeof(SkinManagers.SkinManager), "LightGrayBrush");
            brush = NavigationHelper.GetSkinResourceByKey<SolidColorBrush>(key);
            if (brush == null)
            {
                brush = Brushes.LightGray;
            }
            border.BorderBrush = brush;

            shadowChrome.Color = Colors.Gray;
            border.Child = allContentPresenter;
            shadowChrome.Child = border;
            allItempopup.Child = shadowChrome;
            allItempopup.PlacementTarget = AllNavigationItem;
            allItempopup.VerticalOffset = 35;
            allItempopup.Placement = PlacementMode.Right;
            //Mouse.AddPreviewMouseDownOutsideCapturedElementHandler(shadowChrome, OnOutsideElementCaptured);
            allItempopup.Closed += allItempopup_Closed;

        }

        /// <summary>
        /// Invoked whenever popup window is closed.
        /// </summary>
        /// <param name="sender">Owner of the event.</param>
        /// <param name="e">Event data for the event,</param>
        private void allItempopup_Closed(object sender, EventArgs e)
        {
            allContentPresenter.ClearValue(ContentPresenter.ContentProperty);

        }

        private void BuildAnimation()
        {
            TranslateStoryboard = new Storyboard();

            DAnimation = new DoubleAnimation()
            {
                EasingFunction = new ExponentialEase()
                {
                    EasingMode = EasingMode.EaseOut,
                    Exponent = 5
                }
            };
            DAnimation.Duration = new Duration(TimeSpan.FromSeconds(0.15));

            DAnimation.AutoReverse = true;

            Storyboard.SetTarget(DAnimation, this);

            Storyboard.SetTargetProperty(DAnimation, new PropertyPath("(FrameworkElement.RenderTransform).(TranslateTransform.X)"));

            TranslateStoryboard.Children.Add(DAnimation);

        }

        /// <summary>
        /// Closes drop-down.
        /// </summary>
        private void CloseDropDown()
        {
            if (IsDropDownOpen)
            {
                IsDropDownOpen = false;
            }

            //if item not being dragged and has mouse capture or keyboard focus we release all
            if (!IsDragging)
            {
                Mouse.Capture(null);
                Keyboard.Focus(null);
            }
        }

        protected internal void TryAnimateDropDownContent()
        {
            if (ContentCanBeAnimated)
            {
                if (ParentOutlook == null)
                {
                    ParentOutlook = ItemsControl.ItemsControlFromItemContainer(this) as OutlookBar;
                }

                if (ParentOutlook != null)
                {
                    if (ShowContentStoryboard == null)
                    {
                        BuildShowContentAnimation();
                    }

                    double offset = 0;

                    if (ParentOutlook.IsCompact && ParentOutlook.IsMinimized)
                    {
                        if (!double.IsNaN(DropDownHeight))
                        {
                            VerticalOffset = -DropDownHeight + ActualHeight;
                        }
                        From = -8;
                        To = 0;
                    }
                    else
                    {
                        offset = (-DropDownWidth + ActualWidth) / 2;

                        HorizontalOffset = offset;

                        if (ParentOutlook.IsCompact)
                        {
                            To = 3;
                            From = 20;
                        }
                        else
                        {
                            To = 12;
                            From = 37;
                        }
                        if (image != null)
                        {
                            Point p = image.TranslatePoint(emptyPoint, this);

                            Point p1 = image.TranslatePoint(emptyPoint, ParentOutlook);

                            if (p.X > ActualWidth / 2 + 10)
                            {
                                image.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;

                                image.Margin = new Thickness(p1.X - p.X - 5, -2, 0, 0);
                            }
                        }
                    }
                    if (ParentOutlook.AnimationsEnabled && ParentOutlook.pHelper.CanAnimate)
                    {
                        ShowContentDA.From = From;

                        ShowContentDA.To = To;

                        ShowContentStoryboard.Begin();
                    }
                    else
                    {
                        if (!ParentOutlook.IsCompact || !ParentOutlook.IsMinimized)
                        {
                            VerticalOffset = To;
                        }
                        else
                        {
                            HorizontalOffset = To;
                        }

                        IsDropDownOpen = true;
                    }

                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private void BuildShowContentAnimation()
        {
            ShowContentDA = new DoubleAnimation()
            {
                EasingFunction = new ExponentialEase()
                {
                    EasingMode = EasingMode.EaseOut,

                    Exponent = 5
                }
            };

            Storyboard.SetTargetProperty(ShowContentDA, new PropertyPath(OutlookBarItem.ValueProperty));

            Storyboard.SetTarget(ShowContentDA, this);

            ShowContentStoryboard = new Storyboard();

            ShowContentDA.Duration = new Duration(TimeSpan.FromSeconds(0.35));

            ShowContentStoryboard.Children.Add(ShowContentDA);

            ShowContentStoryboard.Completed += ShowContentStoryboard_Completed;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="isNormal"></param>
        private void BuildScaleAnimation(bool isNormal = true)
        {
            Storyboard sb = new Storyboard();

            DoubleAnimation da = new DoubleAnimation()
            {
                EasingFunction = new ExponentialEase()
                {
                    EasingMode = EasingMode.EaseOut,

                    Exponent = 5
                }
            };

            da.Duration = new Duration(TimeSpan.FromSeconds(0.10));

            Storyboard.SetTargetProperty(da, new PropertyPath(yPropertyPath));

            Storyboard.SetTarget(da, rootControl);

            if (isNormal)
            {
                da.To = 1.0D;
            }
            else
            {
                da.To = 0.85D;
            }

            sb.Children.Add(da);

            da = new DoubleAnimation()
            {
                EasingFunction = new ExponentialEase()
                {
                    EasingMode = EasingMode.EaseOut,
                    Exponent = 5
                }
            };

            da.Duration = new Duration(TimeSpan.FromSeconds(0.10));

            Storyboard.SetTargetProperty(da, new PropertyPath(xPropertyPath));

            Storyboard.SetTarget(da, rootControl);
            if (isNormal)
            {
                da.To = 1.0D;
            }
            else
            {
                da.To = 0.85D;
            }

            sb.Children.Add(da);

            if (isNormal)
            {
                NormalScaleStoryboard = sb;
            }
            else
            {
                SmallScaleStoryborad = sb;
            }
        }

        #endregion

        //------------------------------------------------------
        //
        //  Event Handlers
        //
        //------------------------------------------------------
        #region Event Handlers

        /// <summary>
        /// Occurs directly after Close is called, and can be handled to cancel window closure.
        /// </summary>
        /// <param name="sender">Owner of the event.</param>
        /// <param name="e">Event data for the event.</param>
        private void OnParentWindowClosing(object sender, CancelEventArgs e)
        {
            (sender as Window).Closing -= OnParentWindowClosing;

            if (popup != null)
            {
                popup.Opened -= popup_Loaded;
            }
            if (allItempopup != null)
            {
                allItempopup.Closed -= allItempopup_Closed;
            }

            timer.Tick -= timer_Tick;

            closeDropDowntimer.Tick -= closeDropDowntimer_Tick;

            timer = null;

            closeDropDowntimer = null;

            Mouse.RemovePreviewMouseDownOutsideCapturedElementHandler(this, OnOutsideElementCaptured);

            if (ShowContentStoryboard != null)
            {
                ShowContentStoryboard.Completed -= ShowContentStoryboard_Completed;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void rootControl_MouseLeave(object sender, MouseEventArgs e)
        {
            if (timer.IsEnabled)
            {
                timer.Stop();
            }
            if (IsDropDownOpen && !closeDropDowntimer.IsEnabled)
            {
                closeDropDowntimer.Start();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void rootControl_MouseEnter(object sender, MouseEventArgs e)
        {
            if (EnableDropDown)
            {
                if (closeDropDowntimer.IsEnabled)
                {
                    closeDropDowntimer.Stop();
                }
                timer.Start();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void rootControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {

            if (ParentOutlook == null)
            {
                ParentOutlook = ItemsControl.ItemsControlFromItemContainer(this) as OutlookBar;
                if (ParentOutlook == null)
                {
                    e.Handled = true;
                    return;
                }
            }

            if (timer.IsEnabled)
            {
                timer.Stop();
            }

            if (IsDropDownOpen && !closeDropDowntimer.IsEnabled)
            {
                closeDropDowntimer.Start();
            }
            if (StoryboardsValid && ParentOutlook.AnimationsEnabled)
            {
                scaleChanged = true;
                SmallScaleStoryborad.Begin();
            }

        }

        private void popup_Loaded(object sender, EventArgs e)
        {
            TryAnimateDropDownContent();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AllNavigationItem_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;
            if (!isPopupOpen)
            {
                allContentPresenter.SetValue(ContentPresenter.ContentProperty, InnerContent);
                allItempopup.IsOpen = true;
                isPopupOpen = true;
            }
            else
            {
                isPopupOpen = false;
            }

        }

        /// <summary>
        /// This method is invoked when help popup open finished.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ShowContentStoryboard_Completed(object sender, EventArgs e)
        {
            //If mouse is still over this item, we can open drop down.
            if (IsMouseOver)
            {
                Mouse.Capture(this, CaptureMode.SubTree);
            }
            else
            {
                CloseDropDown();
            }

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void closeDropDowntimer_Tick(object sender, EventArgs e)
        {
            closeDropDowntimer.Stop();

            CloseDropDown();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnOutsideElementCaptured(object sender, MouseButtonEventArgs e)
        {
            if (IsMouseCaptured)
            {
                ReleaseMouseCapture();
            }

            if (IsDropDownOpen)
            {
                IsDropDownOpen = false;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timer_Tick(object sender, EventArgs e)
        {
            timer.Stop();
            if (IsMouseOver &&
                Mouse.LeftButton != MouseButtonState.Pressed &&
                Mouse.RightButton != MouseButtonState.Pressed)
            {
                SetValue(IsDropDownOpenProperty, true);
            }
        }

        #endregion

        //------------------------------------------------------
        //
        //  Fields
        //
        //------------------------------------------------------
        #region Fields

        private const string translateYPropertyPath = "(FrameworkElement.RenderTransform).(TranslateTransform.Y)",
                             translateXPropertyPath = "(FrameworkElement.RenderTransform).(TranslateTransform.X)",
                             yPropertyPath = "(FrameworkElement.RenderTransform).(ScaleTransform.ScaleY)",
                             xPropertyPath = "(FrameworkElement.RenderTransform).(ScaleTransform.ScaleX)",
                             rootControlName = "PART_RootControl",
                             popupName = "PART_OutlookBarItemPopup",
                             imageName = "PART_ItemDownImage";

        private FrameworkElement rootControl;
        private DoubleAnimation DAnimation, ShowContentDA;
        private Storyboard SmallScaleStoryborad,
                            NormalScaleStoryboard,
                            ShowContentStoryboard,
                            TranslateStoryboard;
        private Popup popup;
        private Image image;
        private DispatcherTimer timer = new DispatcherTimer(),
            closeDropDowntimer = new DispatcherTimer();
        private TranslateTransform translate = new TranslateTransform();
        private Point emptyPoint = new Point(0, 0),
            _originScreenCoordPosition,
            _previousScreenCoordPosition,
            _originThumbPoint;

        private SystemDropShadowChrome shadowChrome = null;

        //Double animation's From, To values.
        private double From = 0, To = 0;

        private bool itemMoved = false,
            //
            scaleChanged = false,
            //
            isPopupOpen = false;

        private Popup allItempopup = new Popup()
        {
            AllowsTransparency = true,
            StaysOpen = false,
            PopupAnimation = PopupAnimation.None
        };

        private ContentPresenter allContentPresenter = new ContentPresenter()
        {
            Margin = new Thickness(12)
        };
        internal NavigationItem AllNavigationItem = new NavigationItem()
        {
            Height = 75,
            IsSelectable = false
        };

        internal OutlookBarItemModel _itemModel = null;

        #endregion

        //------------------------------------------------------
        //
        //  Events
        //
        //------------------------------------------------------
        #region Events

        public static readonly RoutedEvent DropDownOpenedEvent = EventManager.RegisterRoutedEvent
           (
               "DropDownOpened",
                RoutingStrategy.Bubble,
                typeof(RoutedEventHandler),
                typeof(OutlookBarItem)
           );

        public event RoutedEventHandler DropDownClosed
        {
            add { AddHandler(DropDownClosedEvent, value); }
            remove { RemoveHandler(DropDownClosedEvent, value); }
        }

        public static readonly RoutedEvent DropDownClosedEvent = EventManager.RegisterRoutedEvent
            (
                "DropDownClosed",
                 RoutingStrategy.Bubble,
                 typeof(RoutedEventHandler),
                 typeof(OutlookBarItem)
            );

        /// <summary>
        /// 
        /// </summary>
        [Category("Behavior")]
        public event DragStartedEventHandler DragStarted
        {
            add { this.AddHandler(OutlookBarItem.DragStartedEvent, value); }
            remove { this.RemoveHandler(OutlookBarItem.DragStartedEvent, value); }
        }

        /// <summary>
        /// 
        /// </summary>
        public static readonly RoutedEvent DragStartedEvent = EventManager.RegisterRoutedEvent
            (
            "DragStarted",
             RoutingStrategy.Bubble,
             typeof(DragStartedEventHandler),
             typeof(OutlookBarItem)
            );

        /// <summary>
        /// 
        /// </summary>
        [Category("Behavior")]
        public event DragDeltaEventHandler DragDelta
        {
            add { this.AddHandler(OutlookBarItem.DragDeltaEvent, value); }
            remove { this.RemoveHandler(OutlookBarItem.DragDeltaEvent, value); }
        }


        /// <summary>
        /// 
        /// </summary>
        public static readonly RoutedEvent DragDeltaEvent = EventManager.RegisterRoutedEvent
           (
           "DragDelta",
            RoutingStrategy.Bubble,
            typeof(DragDeltaEventHandler),
            typeof(OutlookBarItem)
           );

        /// <summary>
        /// 
        /// </summary>
        [Category("Behavior")]
        public event DragCompletedEventHandler DragCompeted
        {
            add { this.AddHandler(OutlookBarItem.DragCompletedEvent, value); }
            remove { this.RemoveHandler(OutlookBarItem.DragCompletedEvent, value); }
        }

        /// <summary>
        /// 
        /// </summary>
        public static readonly RoutedEvent DragCompletedEvent = EventManager.RegisterRoutedEvent
           (
           "DragCompleted",
            RoutingStrategy.Bubble,
            typeof(DragCompletedEventHandler),
            typeof(OutlookBarItem)
           );

        #endregion
    }
}
